<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/myblog/images/apple-touch-icon.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/myblog/images/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/myblog/images/favicon-16x16.png">
  <link rel="mask-icon" href="/myblog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/myblog/css/main.css">

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monda:300,300italic,400,400italic,700,700italic%7CRoboto+Slab:300,300italic,400,400italic,700,700italic%7CNoto+Serif+SC:300,300italic,400,400italic,700,700italic%7CAmita:300,300italic,400,400italic,700,700italic%7CMontserrat:300,300italic,400,400italic,700,700italic%7CPT+Mono:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext">

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"wang-chen1119.gitee.io","root":"/myblog/","images":"/myblog/images","scheme":"Pisces","darkmode":false,"version":"8.13.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":true,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/myblog/js/config.js"></script>

    <meta name="description" content="第一章JDK  —-&gt; Java程序设计语言 Java虚拟机 Java类库   用于支持Java程序开发的最小环境  第二章 自动内存管理概述">
<meta property="og:type" content="blog">
<meta property="og:title" content="深入理解JVM读书笔记">
<meta property="og:url" content="http://wang-chen1119.gitee.io/myblog/2022/10/28/Java%E8%99%9A%E6%8B%9F%E6%9C%BA/index.html">
<meta property="og:site_name" content="Yorn&#39;s Notes">
<meta property="og:description" content="第一章JDK  —-&gt; Java程序设计语言 Java虚拟机 Java类库   用于支持Java程序开发的最小环境  第二章 自动内存管理概述">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129104308450.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129112218648.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129123410643.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129124036163.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129130700021.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203162148316.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203165637452.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203170052342.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204165744839.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204220500254.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204220957814.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204221526822.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205174634120.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205180147340.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205180611776.png">
<meta property="og:image" content="c:/Users/%E7%8E%8B%E7%90%9B/AppData/Roaming/Typora/typora-user-images/image-20220205181452915.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205183249815.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205185601113.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210205146436.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210205846919.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210104893.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210218821.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210455440.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210825073.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210211123180.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212201617415.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212202502170.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212204702191.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212213748066.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216200819649.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216210635519.png">
<meta property="og:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216212130215.png">
<meta property="article:published_time" content="2022-10-28T09:00:12.382Z">
<meta property="article:modified_time" content="2022-10-28T09:00:07.220Z">
<meta property="article:author" content="yorn">
<meta property="article:tag" content="JVM">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129104308450.png">


<link rel="canonical" href="http://wang-chen1119.gitee.io/myblog/2022/10/28/Java%E8%99%9A%E6%8B%9F%E6%9C%BA/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://wang-chen1119.gitee.io/myblog/2022/10/28/Java%E8%99%9A%E6%8B%9F%E6%9C%BA/","path":"2022/10/28/Java虚拟机/","title":"深入理解JVM读书笔记"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>深入理解JVM读书笔记 | Yorn's Notes</title>
  






  <noscript>
    <link rel="stylesheet" href="/myblog/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/myblog/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">Yorn's Notes</p>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-首页"><a href="/myblog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-关于"><a href="/myblog/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-标签"><a href="/myblog/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-分类"><a href="/myblog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E4%B8%80%E7%AB%A0"><span class="nav-number">1.</span> <span class="nav-text">第一章</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E4%BA%8C%E7%AB%A0-%E8%87%AA%E5%8A%A8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86"><span class="nav-number">2.</span> <span class="nav-text">第二章 自动内存管理</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A6%82%E8%BF%B0"><span class="nav-number">2.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%95%B0%E6%8D%AE%E5%86%85%E5%AD%98"><span class="nav-number">2.2.</span> <span class="nav-text">运行时数据内存</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8"><span class="nav-number">2.2.1.</span> <span class="nav-text">程序计数器</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#%E4%BE%8B%E5%AD%90"><span class="nav-number">2.2.1.0.1.</span> <span class="nav-text">例子</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88"><span class="nav-number">2.2.2.</span> <span class="nav-text">Java虚拟机栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="nav-number">2.2.3.</span> <span class="nav-text">本地方法栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Java%E5%A0%86"><span class="nav-number">2.2.4.</span> <span class="nav-text">Java堆</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="nav-number">2.2.5.</span> <span class="nav-text">方法区</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="nav-number">2.2.6.</span> <span class="nav-text">运行时常量池</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HotSpot%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AF%B9%E8%B1%A1"><span class="nav-number">2.3.</span> <span class="nav-text">HotSpot虚拟机对象</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA"><span class="nav-number">2.3.1.</span> <span class="nav-text">对象的创建</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80"><span class="nav-number">2.3.2.</span> <span class="nav-text">对象的内存布局</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E5%A4%B4"><span class="nav-number">2.3.2.1.</span> <span class="nav-text">对象头</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AE%9E%E4%BE%8B%E6%95%B0%E6%8D%AE"><span class="nav-number">2.3.2.2.</span> <span class="nav-text">实例数据</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%AF%B9%E9%BD%90%E5%A1%AB%E5%85%85"><span class="nav-number">2.3.2.3.</span> <span class="nav-text">对齐填充</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BF%E9%97%AE%E5%AE%9A%E4%BD%8D"><span class="nav-number">2.3.3.</span> <span class="nav-text">对象的访问定位</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8F%A5%E6%9F%84"><span class="nav-number">2.3.3.1.</span> <span class="nav-text">句柄</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%9B%B4%E6%8E%A5%E6%8C%87%E9%92%88%E8%AE%BF%E9%97%AE"><span class="nav-number">2.3.3.2.</span> <span class="nav-text">直接指针访问</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E4%B8%89%E7%AB%A0-%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8%E4%B8%8E%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5"><span class="nav-number">3.</span> <span class="nav-text">第三章 垃圾收集器与内存分配策略</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A6%82%E8%BF%B0-1"><span class="nav-number">3.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E5%B7%B2%E6%AD%BB"><span class="nav-number">3.2.</span> <span class="nav-text">对象已死?</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E7%AE%97%E6%B3%95"><span class="nav-number">3.2.1.</span> <span class="nav-text">引用计数算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90%E7%AE%97%E6%B3%95"><span class="nav-number">3.2.2.</span> <span class="nav-text">可达性分析算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%86%8D%E8%B0%88%E5%BC%95%E7%94%A8"><span class="nav-number">3.2.3.</span> <span class="nav-text">再谈引用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%9F%E5%AD%98%E8%BF%98%E6%98%AF%E6%AD%BB%E4%BA%A1"><span class="nav-number">3.2.4.</span> <span class="nav-text">生存还是死亡</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9B%9E%E6%94%B6%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="nav-number">3.2.5.</span> <span class="nav-text">回收方法区</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95"><span class="nav-number">3.3.</span> <span class="nav-text">垃圾收集算法</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%86%E4%BB%A3%E6%94%B6%E9%9B%86%E7%90%86%E8%AE%BA"><span class="nav-number">3.3.1.</span> <span class="nav-text">分代收集理论</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95"><span class="nav-number">3.3.2.</span> <span class="nav-text">标记 - 清除算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%87%E8%AE%B0-%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-number">3.3.3.</span> <span class="nav-text">标记-复制算法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%87%E8%AE%B0-%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-number">3.3.4.</span> <span class="nav-text">标记-整理算法</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HotSpot%E7%9A%84%E7%AE%97%E6%B3%95%E7%BB%86%E8%8A%82"><span class="nav-number">3.4.</span> <span class="nav-text">HotSpot的算法细节</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A0%B9%E8%8A%82%E7%82%B9%E6%9E%9A%E4%B8%BE"><span class="nav-number">3.4.1.</span> <span class="nav-text">根节点枚举</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%89%E5%85%A8%E7%82%B9"><span class="nav-number">3.4.2.</span> <span class="nav-text">安全点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%89%E5%85%A8%E5%8C%BA%E5%9F%9F"><span class="nav-number">3.4.3.</span> <span class="nav-text">安全区域</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%B0%E5%BF%86%E9%9B%86%E5%92%8C%E5%8D%A1%E8%A1%A8"><span class="nav-number">3.4.4.</span> <span class="nav-text">记忆集和卡表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%86%99%E5%B1%8F%E9%9A%9C"><span class="nav-number">3.4.5.</span> <span class="nav-text">写屏障</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B9%B6%E5%8F%91%E7%9A%84%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90"><span class="nav-number">3.4.6.</span> <span class="nav-text">并发的可达性分析</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BB%8F%E5%85%B8%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.</span> <span class="nav-text">经典垃圾收集器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Serial%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.1.</span> <span class="nav-text">Serial收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ParNew%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.2.</span> <span class="nav-text">ParNew收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Parallel-Scavenge%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.3.</span> <span class="nav-text">Parallel Scavenge收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Serial-Old%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.4.</span> <span class="nav-text">Serial Old收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Parallel-Old%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.5.</span> <span class="nav-text">Parallel Old收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#CMS%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.6.</span> <span class="nav-text">CMS收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Garbage-First%E6%94%B6%E9%9B%86%E5%99%A8%EF%BC%88G1%EF%BC%89%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.5.7.</span> <span class="nav-text">Garbage First收集器（G1）收集器</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BD%8E%E5%BB%B6%E8%BF%9F%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.6.</span> <span class="nav-text">低延迟垃圾收集器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Shenandoah%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.6.1.</span> <span class="nav-text">Shenandoah收集器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ZGC%E6%94%B6%E9%9B%86%E5%99%A8"><span class="nav-number">3.6.2.</span> <span class="nav-text">ZGC收集器</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%B8%8E%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5"><span class="nav-number">3.7.</span> <span class="nav-text">内存分配与回收策略</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E5%9C%A8Eden%E5%88%86%E9%85%8D"><span class="nav-number">3.7.1.</span> <span class="nav-text">对象在Eden分配</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A4%A7%E5%AF%B9%E8%B1%A1%E7%9B%B4%E6%8E%A5%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3"><span class="nav-number">3.7.2.</span> <span class="nav-text">大对象直接进入老年代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%95%BF%E6%9C%9F%E5%AD%98%E6%B4%BB%E7%9A%84%E5%AF%B9%E8%B1%A1%E5%B0%86%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3"><span class="nav-number">3.7.3.</span> <span class="nav-text">长期存活的对象将进入老年代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E7%9A%84%E5%B9%B4%E9%BE%84%E5%88%A4%E5%AE%9A"><span class="nav-number">3.7.4.</span> <span class="nav-text">动态的年龄判定</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D%E6%8B%85%E4%BF%9D"><span class="nav-number">3.7.5.</span> <span class="nav-text">空间分配担保</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E5%9B%9B%E7%AB%A0%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7%E3%80%81%E6%95%85%E9%9A%9C%E5%A4%84%E7%90%86%E5%B7%A5%E5%85%B7"><span class="nav-number">4.</span> <span class="nav-text">第四章虚拟机性能监控、故障处理工具</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9F%BA%E7%A1%80%E6%95%85%E9%9A%9C%E5%A4%84%E7%90%86%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.</span> <span class="nav-text">基础故障处理工具</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#jps%EF%BC%9A%E8%99%9A%E6%8B%9F%E6%9C%BA%E8%BF%9B%E7%A8%8B%E7%8A%B6%E5%86%B5%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.1.</span> <span class="nav-text">jps：虚拟机进程状况工具</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jstat%EF%BC%9A%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF%E7%9B%91%E8%A7%86%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.2.</span> <span class="nav-text">jstat：虚拟机统计信息监视工具</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jinfo-Java%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.3.</span> <span class="nav-text">jinfo: Java配置信息工具</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jmap-Java%E5%86%85%E5%AD%98%E6%98%A0%E5%83%8F%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.4.</span> <span class="nav-text">jmap:Java内存映像工具</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jhat-%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%A0%86%E8%BD%AC%E5%82%A8%E5%BF%AB%E7%85%A7%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.5.</span> <span class="nav-text">jhat:虚拟机堆转储快照分析工具</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jstack%EF%BC%9AJava%E5%A0%86%E6%A0%88%E8%B7%9F%E8%B8%AA%E5%B7%A5%E5%85%B7"><span class="nav-number">4.1.6.</span> <span class="nav-text">jstack：Java堆栈跟踪工具</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E5%85%AD%E7%AB%A0-%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84"><span class="nav-number">5.</span> <span class="nav-text">第六章 类文件结构</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Class%E7%B1%BB%E6%96%87%E4%BB%B6%E7%9A%84%E7%BB%93%E6%9E%84"><span class="nav-number">5.1.</span> <span class="nav-text">Class类文件的结构</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%AD%94%E6%95%B0%E4%B8%8EClass%E6%96%87%E4%BB%B6%E7%9A%84%E7%89%88%E6%9C%AC"><span class="nav-number">5.1.0.1.</span> <span class="nav-text">魔数与Class文件的版本</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="nav-number">5.1.1.</span> <span class="nav-text">常量池</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%BF%E9%97%AE%E6%A0%87%E5%BF%97"><span class="nav-number">5.1.2.</span> <span class="nav-text">访问标志</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E4%B8%83%E7%AB%A0-%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6"><span class="nav-number">6.</span> <span class="nav-text">第七章 虚拟机类加载机制</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A6%82%E8%BF%B0-2"><span class="nav-number">6.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E6%97%B6%E6%9C%BA"><span class="nav-number">6.2.</span> <span class="nav-text">类加载的时机</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B"><span class="nav-number">6.3.</span> <span class="nav-text">类加载的过程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A0%E8%BD%BD"><span class="nav-number">6.3.1.</span> <span class="nav-text">加载</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%AA%8C%E8%AF%81"><span class="nav-number">6.3.2.</span> <span class="nav-text">验证</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%AA%8C%E8%AF%81%E9%98%B6%E6%AE%B5-%E2%80%94-gt-%E5%9B%9B%E4%B8%AA%E9%98%B6%E6%AE%B5%E7%9A%84%E6%A3%80%E9%AA%8C%E5%8A%A8%E4%BD%9C"><span class="nav-number">6.3.2.1.</span> <span class="nav-text">验证阶段 —&gt; 四个阶段的检验动作</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%87%86%E5%A4%87"><span class="nav-number">6.3.3.</span> <span class="nav-text">准备</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%A7%A3%E6%9E%90"><span class="nav-number">6.3.4.</span> <span class="nav-text">解析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="nav-number">6.3.5.</span> <span class="nav-text">初始化</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">6.4.</span> <span class="nav-text">类加载器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E4%B8%8E%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">6.4.1.</span> <span class="nav-text">类与类加载器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6"><span class="nav-number">6.4.2.</span> <span class="nav-text">双亲委派机制</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%AC%AC%E5%85%AB%E7%AB%A0-%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AD%97%E8%8A%82%E7%A0%81%E6%89%A7%E8%A1%8C%E5%BC%95%E6%93%8E"><span class="nav-number">7.</span> <span class="nav-text">第八章 虚拟机字节码执行引擎</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%A0%88%E5%B8%A7%E7%BB%93%E6%9E%84"><span class="nav-number">7.1.</span> <span class="nav-text">运行时栈帧结构</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8"><span class="nav-number">7.1.1.</span> <span class="nav-text">局部变量表</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88"><span class="nav-number">7.1.2.</span> <span class="nav-text">操作数栈</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E8%BF%9E%E6%8E%A5"><span class="nav-number">7.1.3.</span> <span class="nav-text">动态连接</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="yorn"
      src="/myblog/uploads/head.jpg">
  <p class="site-author-name" itemprop="name">yorn</p>
  <div class="site-description" itemprop="description">足够专注才能成功</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/myblog/archives/">
          <span class="site-state-item-count">53</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/myblog/categories/">
        <span class="site-state-item-count">13</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/myblog/tags/">
        <span class="site-state-item-count">19</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author site-overview-item animated">
      <span class="links-of-author-item">
        <a href="https://github.com/wang-chen1119" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;wang-chen1119" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:w1905989051@gmail.com" title="E-Mail → mailto:w1905989051@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license site-overview-item animated" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" class="cc-opacity" rel="noopener" target="_blank"><img src="https://cdnjs.cloudflare.com/ajax/libs/creativecommons-vocabulary/2020.11.3/assets/license_badges/small/by_nc_sa.svg" alt="Creative Commons"></a>
  </div>


  <div class="links-of-blogroll site-overview-item animated">
    <div class="links-of-blogroll-title"><i class="fa fa-globe fa-fw"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="https://blog.csdn.net/qq_51088445" title="https:&#x2F;&#x2F;blog.csdn.net&#x2F;qq_51088445" rel="noopener" target="_blank">CSDN</a>
        </li>
    </ul>
  </div>

        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>
  <a role="button" class="book-mark-link book-mark-link-fixed"></a>

  <a href="https://github.com/wang-chen1119" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://wang-chen1119.gitee.io/myblog/2022/10/28/Java%E8%99%9A%E6%8B%9F%E6%9C%BA/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/myblog/uploads/head.jpg">
      <meta itemprop="name" content="yorn">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Yorn's Notes">
      <meta itemprop="description" content="足够专注才能成功">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="深入理解JVM读书笔记 | Yorn's Notes">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          深入理解JVM读书笔记
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2022-10-28 17:00:12 / 修改时间：17:00:07" itemprop="dateCreated datePublished" datetime="2022-10-28T17:00:12+08:00">2022-10-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/myblog/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>22k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>20 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="第一章"><a href="#第一章" class="headerlink" title="第一章"></a>第一章</h1><p>JDK  —-&gt; Java程序设计语言 Java虚拟机 Java类库   用于支持Java程序开发的最小环境</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129104308450.png" alt="image-20220129104308450"></p>
<h1 id="第二章-自动内存管理"><a href="#第二章-自动内存管理" class="headerlink" title="第二章 自动内存管理"></a>第二章 自动内存管理</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>C C++ 对内存管理拥有最高权力，拥有每一个对象的所有权 ，也承担着每一个对象声明从开始到终结的维护任务</p>
<p>Java  虚拟机自动内存管理  不容易出现内存泄漏和溢出的问题   但一旦出现，控制内存的权力交给了Java虚拟机，排查错误修改问题会很难</p>
<h2 id="运行时数据内存"><a href="#运行时数据内存" class="headerlink" title="运行时数据内存"></a>运行时数据内存</h2><p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129112218648.png" alt="image-20220129112218648"></p>
<h3 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h3><blockquote>
<p>程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的 字节码的行号指示器。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器 的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器，分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。   —- 来自《深入理解Java虚拟机》</p>
</blockquote>
<p>三个特点</p>
<ol>
<li>如果线程正在执行的是一个Java方法，这个技术器记录的是正执行的虚拟机字节码指令的地址</li>
<li>如果执行的是本地方法(Native)方法，这个计数器的值应为空</li>
<li>程序计数器是《Java虚拟机规范》中没有规定任何OOM情况的区域</li>
</ol>
<h5 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h5><p>来看一段代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">demo</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>javap进行反编译</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129123410643.png" alt="image-20220129123410643"></p>
<p>当执行到当前方法时，当前的线程中会创建对应的程序计数器，存放执行地址 —-&gt; 上图红框中的内容</p>
<p>进一步理解</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129124036163.png" alt="image-20220129124036163"></p>
<p>我们知道线程是CPU进行调度的最小单位，在一个时间内处理机只会分配给一个线程执行指令</p>
<p>所以当线程1正在占有CPU时，线程2进来且优先级比线程1要高，所以我们把线程1挂起，而这时候线程1的程序计数器中存储线程1的执行位置，</p>
<p>当线程2执行完后，唤醒线程1同时查看线程1的程序计数器，恢复指令上下文。</p>
<p><strong>如果正在执行的是本地（Native）方法，这个计数器值则应为空（Undefined）？</strong></p>
<p>因为程序计数器记录的是字节码文件的指令地址，而本地方法大多是C实现未编译成需要指定的字节码指令</p>
<h3 id="Java虚拟机栈"><a href="#Java虚拟机栈" class="headerlink" title="Java虚拟机栈"></a>Java虚拟机栈</h3><blockquote>
<p>Java虚拟机栈是线程私有的，声明周期与线程同步</p>
<p>每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧Stack Frame）用于存储局部变量表、操作数栈、动态连接、方法出口等信 </p>
<p>息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。</p>
<p>—–来自《深入理解Java虚拟机》</p>
</blockquote>
<p><strong>重点是局部变量表</strong></p>
<p>局部变量表中存放了编译期可知的各种java虚拟机基本数据类型(整数类型，布尔类型，字符类型)和对象引用类型(不等于于对象本身，可能是一个指向对象起始地址的引用指针，也可以是句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)</p>
<p>这些数组类型在局部变量表中的存储空间以局部变量槽(Slot)来表示（64位的long和double占用两个变量槽，其他数据类型只占用一个）。</p>
<p>局部变量表所需的内存空间在编译期间完成分配。</p>
<p>Java虚拟机栈有StackOverflowError异常和OutOfMemoryError异常</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220129130700021.png" alt="image-20220129130700021"></p>
<h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><blockquote>
<p>本地方法栈（Native Method Stacks）与虚拟机栈所发挥的作用是非常相似的，其区别只是虚拟机栈为虚拟机执行Java方法（也就是字节码）服务，而本地方法栈则是为虚拟机使用到的本地（Native）方法服务。 </p>
<p>—-来自《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="Java堆"><a href="#Java堆" class="headerlink" title="Java堆"></a>Java堆</h3><p>Java堆是被所有线程共享的一块内存区域，在虚拟机启动时创建。</p>
<p>唯一目的是存放对象实例。</p>
<p>Java世界内几乎所有的对象实例都在堆里分配内存</p>
<p>Java堆是垃圾收集器管理的内存区域，所以也被称为GC堆</p>
<p>从分配内存的角度看，所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区</p>
<p>将Java堆细分的目的是为了更好的回收内存，或者更快的分配内存</p>
<h3 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h3><p>各个线程共享</p>
<p>用于存储已被虚拟机加载的类型信息，常量，静态变量，即时编译器编译后的代码缓存等数据。</p>
<p>JDK 7的HotSpot，已经把原本放在永久代的字符串常量池、静态变量等移出，而到了 JDK 8，终于完全废弃了永久代的概念，改用与JRockit、J9一样在本地内存中实现的元空间（Meta- space）来代替，把JDK 7中永久代还剩余的内容（主要是类型信息）全部移到元空间中。</p>
<h3 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h3><p>方法区的一部分</p>
<p>Class文件中除了有类的版本、字 段、方法、接口等描述信息外，还有一项信息是常量池表（Constant Pool Table），用于存放编译期生成的各种字面量与符号引用，这部分内容将在类加载后存放到方法区的运行时常量池中。 </p>
<h2 id="HotSpot虚拟机对象"><a href="#HotSpot虚拟机对象" class="headerlink" title="HotSpot虚拟机对象"></a>HotSpot虚拟机对象</h2><h3 id="对象的创建"><a href="#对象的创建" class="headerlink" title="对象的创建"></a>对象的创建</h3><p>Java对象的创建我们最熟悉的就是new一个对象了，对象的创建在虚拟机中是怎样一个过程呢？</p>
<ol>
<li>java虚拟机遇到一条字节码new指令时，首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用，并且检查这个符号引用代表的类是否已经被加载、解析和初始化过，如果没有将先执行响应的类加载过程</li>
<li>虚拟机为新生对象分配内存   —&gt; 对象所需内存的大小在类加载完成后便可以完全确定  —&gt;把一块确定大小的内存块从Java堆中划分出来</li>
<li>内存分配完成后，虚拟机将分配到的内存空间都初始化为零值，如果使用<strong>本地线程分配缓冲</strong>，这项工作也可以在<strong>本地线程分配缓冲</strong>中顺便进行</li>
<li>Java虚拟机对对象进行必要的设置 —&gt; 这个对象是哪个类的实例，如何才能找到类的原数据信息，对象的哈希码，对象的GC分代年龄等信息。</li>
<li>虚拟机角度一个新的对象已经产生了。 从Java程序的视角看，对象创建刚刚开始 —&gt; Class文件中的<init>()方法还没有执行，所有字段默认为零值</li>
<li><init>()方法执行后，一个真正可用的对象才算完全被构造出来</li>
</ol>
<p><strong>指针碰撞</strong></p>
<p>在java堆中内存是绝对规整的，所有被使用的内存都放在一边，空闲的内存被放在另一边，中间放着一个指针作为分界点的指示器，分配内存就是紧紧把那个指针向空闲方向挪动一段与对象大小相等的距离。</p>
<p><strong>空闲链表</strong></p>
<p>如果不是规整的，已被使用的内存和空闲的内存相互交错在一起，那么虚拟机就需要维护一个列表，记录上哪些内存块是可用的，在分配的时候从链表中找到一块足够大的空间划分给对象实例</p>
<p><strong>本地线程分配缓冲(TLAB)</strong></p>
<p>每个线程在Java堆中预先分配一小块内存，称为本地线程分配缓冲，那个线程要分配内存，就在那个线程的本地缓冲区分配，只有本地缓冲区用完了，分配新的缓存区时才需要同步锁定。</p>
<h3 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h3><blockquote>
<p>对象在堆内存中的存储布局 —&gt; 三部分: 对象头，实例数据，对齐填充</p>
</blockquote>
<h4 id="对象头"><a href="#对象头" class="headerlink" title="对象头"></a>对象头</h4><p>包括两类信息</p>
<ol>
<li><p>用于存储对象自身的运行时数据，如哈希码，GC分代年龄、锁状态标志，线程持有的锁、偏向时间戳等  官方称这部分为”Mark Word”</p>
<p> 对象头里的信息与对象自身定义的数据无关的额外成本，考虑到虚拟机的空间效率，Mark Word被设计成一个<strong>有着动态定义的数据结构</strong></p>
<p> <img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203162148316.png" alt="image-20220203162148316"></p>
</li>
<li><p>类型指针 对象指向它的类型元数据的指针</p>
<p> 如果对象是数组，对象头还必须有一块用于记录数组长度的数据</p>
</li>
</ol>
<h4 id="实例数据"><a href="#实例数据" class="headerlink" title="实例数据"></a>实例数据</h4><p>对象真正存储的有效信息（我们在程序代码里面所定义的各种类型的字 段内容，无论是从父类继承下来的，还是在子类中定义的字段都必须记录起来）</p>
<h4 id="对齐填充"><a href="#对齐填充" class="headerlink" title="对齐填充"></a>对齐填充</h4><p>不是必然存在的，也没有特别的含义，仅仅起着占位符的作用。</p>
<p>Hotspot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍，对象头部分已经被设计成了8字节的倍数，如果对象实例数据部分没有对齐的话，就需要通过对齐填充来补全。</p>
<h3 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h3><blockquote>
<p>Java程序会通过栈上的reference数据来操作堆上的具体对象。</p>
<p>reference类型在《Java虚拟机》里只规定了他是一个指向对象的引用，并没有定义这个引用应该通过什么方式定位、访问到堆中对象的具体位置</p>
<p>主流的两种访问方式 ：句柄 和 直接指针</p>
</blockquote>
<h4 id="句柄"><a href="#句柄" class="headerlink" title="句柄"></a>句柄</h4><p>使用句柄访问，java堆中会划分出一块内存来作为句柄池，reference中存储的就是对象的句柄地址，句柄中包含了对象实例数据和类型数据各自具体的地址信息</p>
<p><strong>好处：reference中存储的是稳定句柄地地址，对象被移动时只会改变句柄中实例数据指针，reference本身不需要被修改</strong> </p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203165637452.png" alt="image-20220203165637452"></p>
<h4 id="直接指针访问"><a href="#直接指针访问" class="headerlink" title="直接指针访问"></a>直接指针访问</h4><p>如果使用直接指针访问的话，Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关 信息，reference中存储的直接就是对象地址，如果只是访问对象本身的话，就不需要多一次间接访问的开销</p>
<p><strong>好处：访问速度快，节省了一次指针定位的开销</strong></p>
<p>Hotspot虚拟机主要使用第二种方式进行对象访问</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220203170052342.png" alt="image-20220203170052342"></p>
<h1 id="第三章-垃圾收集器与内存分配策略"><a href="#第三章-垃圾收集器与内存分配策略" class="headerlink" title="第三章 垃圾收集器与内存分配策略"></a>第三章 垃圾收集器与内存分配策略</h1><h2 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h2><p><strong>专业名词  GC(Garbage Collection) 垃圾收集</strong> </p>
<p>垃圾收集需要完成的三件事情</p>
<ul>
<li>哪些内存需要回收</li>
<li>什么时候回收</li>
<li>如何回收</li>
</ul>
<p>垃圾收集所关注的部分 —&gt; 堆和方法区</p>
<p>程序计数器 虚拟机栈 本地方法栈 三个区域是随线程而生，随线程而灭的。每一个栈帧中分配多少内存基本上是在类结构确定下来就可知的，这几个区域的内存分配和回收具有确定性</p>
<p>堆和方法区中的内存分配和回收具有显著的不确定性一个接口的多个实现类需要的内存可能会不一样，一个方法所执行的不同条件分支所需要的内存也可能不一样，只有处于运行期间，我们才 能知道程序究竟会创建哪些对象，创建多少个对象，这部分内存的分配和回收是动态的。</p>
<h2 id="对象已死"><a href="#对象已死" class="headerlink" title="对象已死?"></a>对象已死?</h2><blockquote>
<p>在堆里面存放着Java世界中几乎所有的对象实例，垃圾收集器在对堆进行回收前，第一件事情就是要确定这些对象之中哪些还“存活”着，哪些已经“死去”（“死去”即不可能再被任何途径使用的对象）了</p>
</blockquote>
<h3 id="引用计数算法"><a href="#引用计数算法" class="headerlink" title="引用计数算法"></a>引用计数算法</h3><p>在对象中添加一个引用计数器，每当一个地方引用他计数器就加一，引用失效是计数器就减一。</p>
<p>好处 ：原理简单，判定效率高</p>
<p>坏处 ：必须配合大量的额外处理才能确保正确的工作</p>
<p>如对象循环引用的情况就不能单凭一个引用计数器使用。</p>
<h3 id="可达性分析算法"><a href="#可达性分析算法" class="headerlink" title="可达性分析算法"></a>可达性分析算法</h3><blockquote>
<p>当前主流的商用程序语言（Java、C#，上溯至前面提到的古老的Lisp）的内存管理子系统，都是 通过可达性分析（Reachability Analysis）算法来判定对象是否存活的。 —《深入理解Java虚拟机》</p>
</blockquote>
<p>通过一系列称为“GC Roots”的根对象作为起始节点集，从这些节点开始根据引用关系向下搜索，走过的路径称为因用力按，如果某个对象到GC Roots直接没用任何引用链，则证明此对象不能再被使用。</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204165744839.png" alt="image-20220204165744839"></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">在Java技术体系里面，固定可作为GC Roots的对象包括以下几种： </span><br><span class="line"></span><br><span class="line">·在虚拟机栈（栈帧中的本地变量表）中引用的对象，譬如各个线程被调用的方法堆栈中使用到的 </span><br><span class="line"></span><br><span class="line">参数、局部变量、临时变量等。 </span><br><span class="line"></span><br><span class="line">·在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。 </span><br><span class="line"></span><br><span class="line">·在方法区中常量引用的对象，譬如字符串常量池（</span><br><span class="line">String Table）里的引用。·在本地方法栈中JNI（即通常所说的Native方法）引用的对象。 </span><br><span class="line"></span><br><span class="line">·Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象（比如 NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。 </span><br><span class="line"></span><br><span class="line">·所有被同步锁（synchronized关键字）持有的对象。 </span><br><span class="line"></span><br><span class="line">·反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。 </span><br><span class="line"></span><br><span class="line">---来自《深入理解Java虚拟机》</span><br></pre></td></tr></table></figure>

<h3 id="再谈引用"><a href="#再谈引用" class="headerlink" title="再谈引用"></a>再谈引用</h3><blockquote>
<p>在JDK 1.2版之后，Java对引用的概念进行了扩充，将引用分为强引用（Strongly Re-ference）、软 引用（Soft Reference）、弱引用（Weak Reference）和虚引用（Phantom Reference）4种，这4种引用强 度依次逐渐减弱。  —-来自《深入理解Java虚拟机》</p>
</blockquote>
<ul>
<li><p>强引用</p>
<p>  最传统的“引用”的定义，普遍存在的引用赋值如</p>
  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br></pre></td></tr></table></figure>

<p>  无论什么情况下，只要强引用关系还在，垃圾收集器就永远不会收掉被引用的对象</p>
</li>
<li><p>软引用</p>
<blockquote>
<p>软引用是用来描述一些还有用，但非必须的对象。只被软引用关联着的对象，在系统将要发生内 存溢出异常前，会把这些对象列进回收范围之中进行第二次回收，如果这次回收还没有足够的内存， 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。 —《深入理解Java虚拟机》</p>
</blockquote>
</li>
</ul>
<p>即将要发生内存溢出异常时，会先把软引用对象进行回收，再进行判断如果内存还不够，才抛出内存异常溢出</p>
<ul>
<li><p>弱引用</p>
<p>  用来描述非必须对象，被弱引用关联的对象只能生存到下一次垃圾收集。垃圾收集开始时，无论内存是否足够都会回收被弱引用关联的对象</p>
<p>  在JDK 1.2版之后提供了SoftReference类来实现软引用</p>
</li>
<li><p>虚引用(幽灵引用或者幻影引用)</p>
</li>
</ul>
<blockquote>
<p>它是最弱的一种引用关系。一个对象是否有虚引用的存在，完全不会对其生存时间构成影响，也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供 了PhantomReference类来实现虚引用   —-《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="生存还是死亡"><a href="#生存还是死亡" class="headerlink" title="生存还是死亡"></a>生存还是死亡</h3><p>真正宣告一个对象死亡至少要经历两次标记过程</p>
<ol>
<li><p>如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链，会被第一次标记</p>
</li>
<li><p>第一次标记后进行一次筛选，条件为是否有必要执行finalize方法。 对象没有覆盖finalize()方法，或者方法已经被虚拟机调用过都被虚拟机视为“没有必要执行”</p>
</li>
<li><p>有必要 -&gt; 防止在F-Queue队列中，由虚拟机自动建立的、低调度优先级的Finalizer线程执行他们的</p>
<p> finalize() </p>
</li>
<li><p>稍后收集器对F-Queue中的对象进行第二次小规模的标记，自救成功地对象将被移出即将回收的集合</p>
</li>
<li><p>没自救成功就会被回收</p>
</li>
</ol>
<p><em><strong>任何一个对象的finalize()方法都只会被系统自动调用一次</strong></em></p>
<h3 id="回收方法区"><a href="#回收方法区" class="headerlink" title="回收方法区"></a>回收方法区</h3><p>主要回收两部分内容 ： 1. 废弃的常量和不再使用的类型</p>
<p>判断一个常量是否需要被回收的条件</p>
<p>已经没有任何对象引用这个常量，虚拟机也没有其他地方引用这个字面量</p>
<p>判断一个类型是否属于“不再被使用的类”的条件</p>
<ul>
<li><p>该类所有的实例都已经被回收，也就是Java堆中不存在该类及其任何派生子类的实例。 </p>
</li>
<li><p>加载该类的类加载器已经被回收，这个条件除非是经过精心设计的可替换类加载器的场景，如OSGi、JSP的重加载等，否则通常是很难达成的。 </p>
</li>
<li><p>该类对应的java.lang.Class对象没有在任何地方被引用，无法在任何地方通过反射访问该类的方法。</p>
</li>
</ul>
<p><strong>java虚拟机被允许回收满足上面三个条件的无用类，而不是必然被回收关于是否要对类型进行回收，HotSpot虚拟机提供了- Xnoclassgc参数进行控制，还可以使用-verbose：class以及-XX：+TraceClass-Loading、-XX： +TraceClassUnLoading查看类加载和卸载信息，其中-verbose：class和-XX：+TraceClassLoading可以在 Product版的虚拟机中使用，-XX：+TraceClassUnLoading参数需要FastDebug版[1]的虚拟机支持。</strong></p>
<h2 id="垃圾收集算法"><a href="#垃圾收集算法" class="headerlink" title="垃圾收集算法"></a>垃圾收集算法</h2><blockquote>
<p>从如何判定对象消亡的角度出发，垃圾收集算法可以划分为“引用计数式垃圾收集”（Reference Counting GC）和“追踪式垃圾收集”（Tracing GC）两大类，这两类也常被称作“直接垃圾收集”和“间接 垃圾收集”。 —《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="分代收集理论"><a href="#分代收集理论" class="headerlink" title="分代收集理论"></a>分代收集理论</h3><p>当前商业虚拟机的垃圾收集器，大多数都遵循了“分代收集”的理论进行设计。分代收集建立在两个分代假说之上：</p>
<ol>
<li>弱分代假说：绝大多数对象都是朝生夕灭的</li>
<li>强分代假说：熬过越多次垃圾收集过程的对象就越难以消灭</li>
</ol>
<p>两个假说又共同奠定了一个设计原则 ： 收集器应该将Java堆划分出不同的区域，然后将回收对象根据年龄(熬过垃圾收集过程的次数)分配到不同的区域之中存储。</p>
<p>将分代理论具体放在现在商用的Java虚拟机里，设计者一般至少会把Java堆划分为新生代和老年代两个区域。</p>
<p>为了解决跨代引用的问题，需要对分代收集理论添加第三条经验法则：</p>
<ol start="3">
<li><p>跨代引用假说（Intergenerational Reference Hypothesis）：跨代引用相对于同代引用来说仅占极少数。</p>
<p> 根据这条假说，我们就不再为了少量跨代引用去遍历老年代，也不用浪费空间专门记录每一个对象是否存在和存在哪些跨代引用。</p>
<p> 只需要在新生代上建立一个全局的数据结构(记忆集)，这个结构把老年代分成若干小块，表示老年代哪一块存在跨代引用，在出现新生代的GC时，只需要将包含了跨代引用的小块内存中的对象加入到GC Roots进行扫描</p>
</li>
</ol>
<blockquote>
<p>存在互相引用关系的两个对象应该是倾向于同时生存或者同时消亡的。如 一个新生代对象存在跨代引用，由于老年代对象难以消亡，导致新生代在收集过程也会存活，随着年龄增长进入老年代，跨代引用也就消失了。</p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">一些名词</span><br><span class="line"></span><br><span class="line">部分收集（Partial GC）：指目标不是完整收集整个Java堆的垃圾收集，其中又分为： </span><br><span class="line"></span><br><span class="line">■新生代收集（Minor GC/Young GC）：指目标只是新生代的垃圾收集。 </span><br><span class="line"></span><br><span class="line">■老年代收集（Major GC/Old GC）：指目标只是老年代的垃圾收集。目前只有CMS收集器会有单 </span><br><span class="line"></span><br><span class="line">独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆，在不同资料上常有不同所指， </span><br><span class="line"></span><br><span class="line">读者需按上下文区分到底是指老年代的收集还是整堆收集。 </span><br><span class="line"></span><br><span class="line">■混合收集（Mixed GC）：指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收 </span><br><span class="line"></span><br><span class="line">集器会有这种行为。 </span><br><span class="line"></span><br><span class="line">·整堆收集（Full GC）：收集整个Java堆和方法区的垃圾收集</span><br></pre></td></tr></table></figure>

<h3 id="标记-清除算法"><a href="#标记-清除算法" class="headerlink" title="标记 - 清除算法"></a>标记 - 清除算法</h3><p>最早最近出的算法，算法分为标记和清除两个阶段： 首先标记出所有需要回收的对象，在标记完成后，统一回收调所有被标记的对象，也能标记存活对象，回收未标记对象。</p>
<p>缺点：</p>
<ol>
<li>执行效率不稳定</li>
<li>内存空间的碎片化问题</li>
</ol>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204220500254.png" alt="image-20220204220500254"></p>
<h3 id="标记-复制算法"><a href="#标记-复制算法" class="headerlink" title="标记-复制算法"></a>标记-复制算法</h3><p>将内存分成等量的两块，每次只使用其中一块，当一块内存用完后，就将还存活着的对象复制到另外一块上面，然后把已使用过的内存空间清理调。</p>
<p>运行简单，但是可用内存缩小为了原来的一般，内存空间浪费过多。</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204220957814.png" alt="image-20220204220957814"></p>
<h3 id="标记-整理算法"><a href="#标记-整理算法" class="headerlink" title="标记-整理算法"></a>标记-整理算法</h3><p>标记整理算法标记过程与标记-清除算法相同，但后续是让所有存活对象向内存空间的一端移动，直接清理掉边界以外的内存。</p>
<blockquote>
<p>如果移动存活对象，尤其是在老年代这种每次回收都有大量对象存活区域，移动存活对象并更新 所有引用这些对象的地方将会是一种极为负重的操作，而且这种对象移动操作必须全程暂停用户应用 程序才能进行，这就更加让使用者不得不小心翼翼地权衡其弊端了，像这样的停顿被最初的虚拟机 设计者形象地描述为“Stop The World”。 —来自《深入理解Java虚拟机》</p>
</blockquote>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220204221526822.png" alt="image-20220204221526822"></p>
<h2 id="HotSpot的算法细节"><a href="#HotSpot的算法细节" class="headerlink" title="HotSpot的算法细节"></a>HotSpot的算法细节</h2><h3 id="根节点枚举"><a href="#根节点枚举" class="headerlink" title="根节点枚举"></a>根节点枚举</h3><blockquote>
<p>迄今为止，所有收集器在根节点枚举这一步骤时都是必须暂停用户线程的，因此毫无疑问根节点 枚举与之前提及的整理内存碎片一样会面临相似的“Stop The World”的困扰。 —《深入理解java虚拟机》</p>
</blockquote>
<p>目前主流Java虚拟机使用的都是准确式的垃圾收集，当用户线程停顿下来之后，并不需要一个不漏地检查完所有执行上下文和全局的引用位置，虚拟机应当是有办法直接得到哪些地方存着对象引用，在hotshop中，采用了一组OopMap的数据结构来解决。</p>
<h3 id="安全点"><a href="#安全点" class="headerlink" title="安全点"></a>安全点</h3><p>使用OopMap也有一些问题存在:</p>
<p>可能导致引用关系变化，或者OopMap内容变化的指令非常多，如果为每一条指令都生成对应的OopMap将会需要大量的额外存储空间。</p>
<p>HotShop并没有为每条指令都生成OopMap，只是在“特定的位置”记录了这些信息，称为安全点</p>
<p>选取标准：是否具有让程序长时间执行的特征</p>
<p>“长时间执行”的最明显特征就是指令序列的复用，例如方法调用、循环跳转、异常跳转 </p>
<p>等都属于指令序列复用，所以只有具有这些功能的指令才会产生安全点。</p>
<p>如何在垃圾收集发生时让所有线程都跑到最近的安全点，然后停顿下来  —&gt; 两种方案 </p>
<ol>
<li><p>抢先式中断</p>
<p> 在垃圾收集发生时，系统首先把所有用户线程全部中断，如果发现有用户线程中断的地 </p>
<p> 方不在安全点上，就恢复这条线程执行，让它一会再重新中断，直到跑到安全点上。现在几乎没有虚 </p>
<p> 拟机实现采用抢先式中断来暂停线程响应GC事件。</p>
</li>
<li><p>主动式中断</p>
<p> 当垃圾收集需要中断线程的时候，不直接对线程进行操作，仅仅设置一个标记位，各个线程执行过程时会不停的去主动轮询这个标志，一旦中断标志位真是，就自己在最近的安全点上主动中断挂起。轮询标志的地方和安全点是重合的，另外还要加上所有创建对象和其他 需要在Java堆上分配内存的地方，这是为了检查是否即将要发生垃圾收集，避免没有足够内存分配新 对象。</p>
</li>
</ol>
<h3 id="安全区域"><a href="#安全区域" class="headerlink" title="安全区域"></a>安全区域</h3><blockquote>
<p>安全区域是指能够确保在某一段代码片段之中，引用关系不会发生变化，因此，在这个区域中任 意地方开始垃圾收集都是安全的。我们也可以把安全区域看作被扩展拉伸了的安全点。 </p>
<p>— 《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="记忆集和卡表"><a href="#记忆集和卡表" class="headerlink" title="记忆集和卡表"></a>记忆集和卡表</h3><p>记忆集： 一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构</p>
<p>在垃圾收集的场景中收集器只需要通过记忆集判断出某一块非收集区域手否存在指向了收集区域的指针就可以，所以就出现了一些记录精度</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">·字长精度：每个记录精确到一个机器字长（就是处理器的寻址位数，如常见的32位或64位，这个 精度决定了机器访问物理内存地址的指针长度），该字包含跨代指针。 </span><br><span class="line">·对象精度：每个记录精确到一个对象，该对象里有字段含有跨代指针。 </span><br><span class="line">·卡精度：每个记录精确到一块内存区域，该区域内有对象含有跨代指针。</span><br></pre></td></tr></table></figure>



<p>卡精度 所指的就是用称为”卡表”的方式去实现记忆集，这是最常用的一种记忆集实现形式</p>
<p>一个卡页的内存中通常包含不止一个对象，只要卡页内有一个（或更多）对象的字段存在着跨代 指针，那就将对应卡表的数组元素的值标识为1，称为这个元素变脏（Dirty），没有则标识为0。</p>
<h3 id="写屏障"><a href="#写屏障" class="headerlink" title="写屏障"></a>写屏障</h3><p>在HotSpot虚拟机里是通过写屏障（Write Barrier）技术维护卡表状态的。</p>
<p>写屏障可以看作在虚拟机层面面对“引用类型字段赋值”动作时的AOP切面，在引用对象赋值是会产生一个环形通知，供程序执行额外的动作。</p>
<p>赋值前 –&gt; 写前屏障  赋值后–&gt; 写后屏障</p>
<h3 id="并发的可达性分析"><a href="#并发的可达性分析" class="headerlink" title="并发的可达性分析"></a>并发的可达性分析</h3><p>三色标记</p>
<p>把遍历对象图过程中遇到的对象，按照“是否访问过”这个条件标记成以下三种颜色： </p>
<p>·白色：表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段，所有的对象都是 白色的，若在分析结束的阶段，仍然是白色的对象，即代表不可达。 </p>
<p>·黑色：表示对象已经被垃圾收集器访问过，且这个对象的所有引用都已经扫描过。黑色的对象代 表已经扫描过，它是安全存活的，如果有其他对象引用指向了黑色对象，无须重新扫描一遍。黑色对 象不可能直接（不经过灰色对象）指向某个白色对象。 </p>
<p>·灰色：表示对象已经被垃圾收集器访问过，但这个对象上至少存在一个引用还没有被扫描过。 </p>
<p>进行可达性分析时，如果用户进程和收集器线程同时工作，会出现两种结果</p>
<ol>
<li>把原本消亡的对象错误标记存活</li>
<li>把原本存活的对象错误标记为消亡</li>
</ol>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205174634120.png" alt="image-20220205174634120"></p>
<p>当且仅当两个条件同时满足时，会产生“对象消失”的问题 即原本应该是黑色的对象被误标为白色</p>
<ol>
<li>赋值器插入了一条或多条黑色对象到白色对象的引用</li>
<li>赋值器删除了全部从灰色对象到该白色对象的直接或间接引用</li>
</ol>
<p>要解决并发扫描时的对象消失问题，只需破坏两个条件中的任意一个即可，由此分别产生了两种解决方案</p>
<ol>
<li><p>增量更新</p>
<p> 简化理解：黑色对象一旦新插入了指向白色对象的引用之后，它就变回灰色对象</p>
</li>
<li><p>原始快照</p>
<p> 简化理解：无论引用关系删除与否，都会按照刚刚开始扫描那一刻的对象图快照来进行搜索</p>
</li>
</ol>
<h2 id="经典垃圾收集器"><a href="#经典垃圾收集器" class="headerlink" title="经典垃圾收集器"></a>经典垃圾收集器</h2><h3 id="Serial收集器"><a href="#Serial收集器" class="headerlink" title="Serial收集器"></a>Serial收集器</h3><p>是一个单线程收集器，它在进行垃圾收集时，必须暂停其他所有工作线程，直到它收集结束。</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205180147340.png" alt="image-20220205180147340"></p>
<blockquote>
<p>对于单核处理 器或处理器核心数较少的环境来说，Serial收集器由于没有线程交互的开销，专心做垃圾收集自然可以 获得最高的单线程收集效率。在用户桌面的应用场景以及近年来流行的部分微服务应用中，分配给虚 拟机管理的内存一般来说并不会特别大，收集几十兆甚至一两百兆的新生代（仅仅是指新生代使用的 内存，桌面应用甚少超过这个容量），垃圾收集的停顿时间完全可以控制在十几、几十毫秒，最多一 百多毫秒以内，只要不是频繁发生收集，这点停顿时间对许多用户来说是完全可以接受的。所以，Serial收集器对于运行在客户端模式下的虚拟机来说是一个很好的选择   —-《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="ParNew收集器"><a href="#ParNew收集器" class="headerlink" title="ParNew收集器"></a>ParNew收集器</h3><p>实质上是Serial收集器的多线程并行版本</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205180611776.png" alt="image-20220205180611776"></p>
<h3 id="Parallel-Scavenge收集器"><a href="#Parallel-Scavenge收集器" class="headerlink" title="Parallel Scavenge收集器"></a>Parallel Scavenge收集器</h3><p>基于标记-复制算法实现，能够并行收集</p>
<p>Parallel Scavenge收集器 的目标是达到一个可控制的吞吐量</p>
<p><img src="C:/Users/%E7%8E%8B%E7%90%9B/AppData/Roaming/Typora/typora-user-images/image-20220205181452915.png" alt="image-20220205181452915"></p>
<p>两个参数控制吞吐量：</p>
<ul>
<li><p>控制最大垃圾收集停顿时间 的-XX：MaxGCPauseMillis参数</p>
</li>
<li><p>直接设置吞吐量大小的-XX：GCTimeRatio参数</p>
</li>
</ul>
<p>自适应的调节策略:</p>
<p>+UseAdaptiveSizePolicy值得我们关注。这是一个开关参数，当这个参数被激活之后，就不需要人工指定新生代的大小（-Xmn）、Eden与Survivor区的比例（-XX：SurvivorRatio）、晋升老年代对象大小（-XX：PretenureSizeThreshold）等细节参数 了，虚拟机会根据当前系统的运行情况收集性能监控信息，动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量</p>
<h3 id="Serial-Old收集器"><a href="#Serial-Old收集器" class="headerlink" title="Serial Old收集器"></a>Serial Old收集器</h3><p>serial收集器的老年代版本，单线程收集器，使用标记-整理算法</p>
<p>两种用途</p>
<ol>
<li>与Parallel Scavenge收集器搭配使用使用</li>
<li>作为CMS失败时的后备预案，在并发收集发生Concurrent Mode Failure时使用</li>
</ol>
<h3 id="Parallel-Old收集器"><a href="#Parallel-Old收集器" class="headerlink" title="Parallel Old收集器"></a>Parallel Old收集器</h3><p>Parallel Old是Parallel Scavenge收集器的老年代版本，支持多线程并发收集，基于标记-整理算法实现</p>
<h3 id="CMS收集器"><a href="#CMS收集器" class="headerlink" title="CMS收集器"></a>CMS收集器</h3><p>一种以获取最短回收停顿时间为目标的收集器</p>
<p>基于标记-清除算法实现，运行过程分为四个步骤</p>
<p>1）初始标记（CMS initial mark）     标记一下GC Roots能直接关联到的对象，速度很快</p>
<p>2）并发标记（CMS concurrent mark）  从GC Roots的直接关联对象开始遍历整个对象图的过程，时间长但不需要停顿用户线程，可以与GC线程并发运行</p>
<p>3）重新标记（CMS remark）     修正并发标记期间，因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录 —&gt; 增量更新</p>
<p>4）并发清除（CMS concurrent sweep） 清理删除掉标记阶段判断的已经死亡的对象 ，可以与用户线程同时并发</p>
<p>四个步骤并发标记和并发清除耗时最长但都可以与用户线程一起工作</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205183249815.png" alt="image-20220205183249815"></p>
<p>缺点：</p>
<ol>
<li><p>对处理器资源非常敏感  并发阶段虽然不会导致用户线程停顿，但会因为占用了一部分线程而导致应用程序变慢</p>
</li>
<li><p>无法处理“浮动垃圾”</p>
<blockquote>
<p>在CMS的并发标记和并发清理阶 段，用户线程是还在继续运行的，程序在运行自然就还会伴随有新的垃圾对象不断产生，但这一部分 垃圾对象是出现在标记过程结束以后，CMS无法在当次收集中处理掉它们，只好留待下一次垃圾收集 时再清理掉。这一部分垃圾就称为“浮动垃圾”。</p>
</blockquote>
</li>
</ol>
<p>这导致CMS收集器需要预留一部分空间供并发收集时的程序运作使用，JDK6是，CMS收集器的启动阈值提升至92%，有着另一种风险：CMS运行时预留的空间无法满足程序分配新对象的需要，会出现一次并发失败，虚拟机不得不启用后备预案，冻结用户进行，采用serial old收集器重新进行老年代的垃圾收集。</p>
<ol start="3">
<li>CMS采用标记-清除算法会产生大量的空间碎片</li>
</ol>
<p>两个参数</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-XX：+UseCMS-CompactAtFullCollection开关参数（默认是开启的，此参数从 JDK 9开始废弃），用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程，由于这个 内存整理必须移动存活对象，（在Shenandoah和ZGC出现前）是无法并发的。这样空间碎片问题是解 决了，但停顿时间又会变长，因此虚拟机设计者们还提供了另外一个参数-XX：CMSFullGCsBefore- Compaction（此参数从JDK 9开始废弃），这个参数的作用是要求CMS收集器在执行过若干次（数量 由参数值决定）不整理空间的Full GC之后，下一次进入Full GC前会先进行碎片整理（默认值为0，表 示每次进入Full GC时都进行碎片整理）。</span><br></pre></td></tr></table></figure>



<h3 id="Garbage-First收集器（G1）收集器"><a href="#Garbage-First收集器（G1）收集器" class="headerlink" title="Garbage First收集器（G1）收集器"></a>Garbage First收集器（G1）收集器</h3><p>开创了收集器面向局部收集的设计思路和基于Region的内存布局形式</p>
<p>目标：</p>
<p>建立起“停顿时间模型”:能够支持指定在一个长度为M毫秒的时间片段 内，消耗在垃圾收集上的时间大概率不超过N毫秒这样的目标</p>
<p>G1将连续的Java堆分为多个大小相等的独立区域(Region)，每一个region可以根据需要扮演新生代的Eden空间、Survivor空间，或者老年代空间</p>
<p>Region的大小可以通过参数-XX：G1HeapRegionSize设 定，取值范围为1MB～32MB，且应为2的N次幂。</p>
<p>Region中有一类特殊的Humongous区域，专门用来存储大对象。G1只要认为大小超过了一个Region容量的一半的对象判定为大对象。</p>
<p>对于标记结果出现错误的问题，G1采用原始快照算法来实现</p>
<p>G1的运作过程</p>
<ol>
<li><p>初始标记 ：仅仅只是标记一下GC Roots能直接关联到的对象，并且修改TAMS 指针的值，让下一阶段用户线程并发运行时，能正确地在可用的Region中分配新对象。需要停顿线程，但耗时很短</p>
</li>
<li><p>并发标记 ： 从GC Root开始对堆中对象进行可达性分析，递归扫描整个堆 里的对象图，找出要回收的对象，这阶段耗时较长，但可与用户程序并发执行。当对象图扫描完成以后，还要重新处理SATB记录下的在并发时有引用变动的对象</p>
</li>
<li><p>最终标记 ： 对用户线程做另一个短暂的暂停，用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。 </p>
</li>
<li><p>筛选回归 ：负责更新Region的统计数据，对各个Region的回 </p>
<p> 收价值和成本进行排序，根据用户所期望的停顿时间来制定回收计划，可以自由选择任意多个Region </p>
<p> 构成回收集，然后把决定回收的那一部分Region的存活对象复制到空的Region中，再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动，是必须暂停用户线程，由多条收集器线程并行完成的。</p>
</li>
</ol>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220205185601113.png" alt="image-20220205185601113"></p>
<h2 id="低延迟垃圾收集器"><a href="#低延迟垃圾收集器" class="headerlink" title="低延迟垃圾收集器"></a>低延迟垃圾收集器</h2><h3 id="Shenandoah收集器"><a href="#Shenandoah收集器" class="headerlink" title="Shenandoah收集器"></a>Shenandoah收集器</h3><p>Shenandoah也是使用基于Region的堆内存布局，同样 有着用于存放大对象的Humongous Region，默认的回收策略也同样是优先处理回收价值最大的 </p>
<p>Region</p>
<p>于G1的不同之处</p>
<ol>
<li>支持并发的整理算法</li>
<li>默认不使用分代收集</li>
<li>摒弃了在G1中耗费大量内存和计算资源去维护的记忆集，改用名为“连接矩阵”（Connection Matrix）的全局数据结构来记录跨Region的引用关系，降低了处理跨代指针时的记忆集维护消耗，也降低了伪共享问题的发生概率 。连接矩阵可以简单理解为一张二维表格，如果Region N有 对象指向Region M，就在表格的N行M列中打上一个标记</li>
</ol>
<p><strong>工作过程</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">·初始标记（Initial Marking）：与G1一样，首先标记与GC Roots直接关联的对象，这个阶段仍 是“Stop The World”的，但停顿时间与堆大小无关，只与GC Roots的数量相关。 </span><br><span class="line">·并发标记（Concurrent Marking）：与G1一样，遍历对象图，标记出全部可达的对象，这个阶段 是与用户线程一起并发的，时间长短取决于堆中存活对象的数量以及对象图的结构复杂程度。 </span><br><span class="line">·最终标记（Final Marking）：与G1一样，处理剩余的SATB扫描，并在这个阶段统计出回收价值 最高的Region，将这些Region构成一组回收集（Collection Set）。最终标记阶段也会有一小段短暂的停 顿。</span><br><span class="line">·并发清理（Concurrent Cleanup）：这个阶段用于清理那些整个区域内连一个存活对象都没有找到 的Region（这类Region被称为Immediate Garbage Region）。 </span><br><span class="line">·并发回收（Concurrent Evacuation）：并发回收阶段是Shenandoah与之前HotSpot中其他收集器的 核心差异。在这个阶段，Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region之 中。复制对象这件事情如果将用户线程冻结起来再做那是相当简单的，但如果两者必须要同时并发进 行的话，就变得复杂起来了。其困难点是在移动对象的同时，用户线程仍然可能不停对被移动的对象 进行读写访问，移动对象是一次性的行为，但移动之后整个内存中所有指向该对象的引用都还是旧对象的地址，这是很难一瞬间全部改变过来的。对于并发回收阶段遇到的这些困难，Shenandoah将会通 过读屏障和被称为“Brooks Pointers”的转发指针来解决（讲解完Shenandoah整个工作过程之后笔者还要 再回头介绍它）。并发回收阶段运行的时间长短取决于回收集的大小。</span><br><span class="line">·初始引用更新（Initial Update Reference）：并发回收阶段复制对象结束后，还需要把堆中所有指 向旧对象的引用修正到复制后的新地址，这个操作称为引用更新。引用更新的初始化阶段实际上并未 做什么具体的处理，设立这个阶段只是为了建立一个线程集合点，确保所有并发回收阶段中进行的收 集器线程都已完成分配给它们的对象移动任务而已。初始引用更新时间很短，会产生一个非常短暂的 停顿。</span><br><span class="line">·并发引用更新（Concurrent Update Reference）：真正开始进行引用更新操作，这个阶段是与用户 线程一起并发的，时间长短取决于内存中涉及的引用数量的多少。并发引用更新与并发标记不同，它 不再需要沿着对象图来搜索，只需要按照内存物理地址的顺序，线性地搜索出引用类型，把旧值改为 新值即可。</span><br><span class="line">·最终引用更新（Final Update Reference）：解决了堆中的引用更新后，还要修正存在于GC Roots 中的引用。这个阶段是Shenandoah的最后一次停顿，停顿时间只与GC Roots的数量相关。</span><br><span class="line">·并发清理（Concurrent Cleanup）：经过并发回收和引用更新之后，整个回收集中所有的Region已 再无存活对象，这些Region都变成Immediate Garbage Regions了，最后再调用一次并发清理过程来回收 这些Region的内存空间，供以后新对象分配使用。</span><br></pre></td></tr></table></figure>





<p>Shenandoah用于支持并行整理的核心概念 Brooks Pointer</p>
<p>使用转发指针来实现对象移动与用户程序并发，之前通常是在被移动对象原有的内存上设置保护陷阱，用户程序一旦访问属于旧对象的内存空间就会产生自毁中断</p>
<p>Brooks提出的新方案不需要用到内存保护陷阱，而是在原有对象布局结构的最前面统一增加一个 新的引用字段，在正常不处于并发移动的情况下，该引用指向对象自己</p>
<h3 id="ZGC收集器"><a href="#ZGC收集器" class="headerlink" title="ZGC收集器"></a>ZGC收集器</h3><p>ZGC收集器是一款基于Region内存布局的，（暂时） 不设分代的，使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的，</p>
<h2 id="内存分配与回收策略"><a href="#内存分配与回收策略" class="headerlink" title="内存分配与回收策略"></a>内存分配与回收策略</h2><h3 id="对象在Eden分配"><a href="#对象在Eden分配" class="headerlink" title="对象在Eden分配"></a>对象在Eden分配</h3><p>一般情况对象在新生代Eden去中分配，Eden去没有足够空间进行分配时，虚拟机发起第一次Minor GC</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HotSpot虚拟机提供了-XX：+PrintGCDetails这个收集器日志参数，告诉虚拟机在发生垃圾收集行 为时打印内存回收日志，并且在进程退出的时候输出当前的存各区域分配情况。</span><br></pre></td></tr></table></figure>



<h3 id="大对象直接进入老年代"><a href="#大对象直接进入老年代" class="headerlink" title="大对象直接进入老年代"></a>大对象直接进入老年代</h3><p>大对象就是指需要大量连续内存空间的Java对象，最典型的大对象便是那种很长的字符串，或者元素数量很庞大的数组</p>
<p>避免大对象的原因：分配空间时容易导致还有不少内存时就提前触发垃圾收集，必须要有足够的连续空间来安置，当复制对象时，大对象就意味着高额的内存复制开销。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HotSpot虚拟机提供了-XX：PretenureSizeThreshold 参数，指定大于该设置值的对象直接在老年代分配，这样做的目的就是避免在Eden区及两个Survivor区 之间来回复制，产生大量的内存复制操作。</span><br></pre></td></tr></table></figure>



<h3 id="长期存活的对象将进入老年代"><a href="#长期存活的对象将进入老年代" class="headerlink" title="长期存活的对象将进入老年代"></a>长期存活的对象将进入老年代</h3><p>对象在Eden中诞生，如果经过一次Minor GC后仍然存活，并且能够被Survivor容纳的话，对象会被移动到Survivor空间中，对象在Survivor区中每熬过一次Minor GC年龄就增加一岁，当他的年龄增加到一定程度（默认15），就会晋升到老年代</p>
<p>通过参数 -XX：MaxTenuringThreshold设置</p>
<h3 id="动态的年龄判定"><a href="#动态的年龄判定" class="headerlink" title="动态的年龄判定"></a>动态的年龄判定</h3><p>如果在Survivor空间中相同年龄所有对象大小的总和大于 Survivor空间的一半，年龄大于或等于该年龄的对象就可以直接进入老年代，无须等到-XX： MaxTenuringThreshold中要求的年龄。</p>
<h3 id="空间分配担保"><a href="#空间分配担保" class="headerlink" title="空间分配担保"></a>空间分配担保</h3><p>发生Minor GC之前虚拟机要先检查一下老年代最大可用的连续空间是否大于新生代所有对象的总空间，条件成立则这次Minor GC确保是安全的，不成立虚拟机会先查看看<code>-XX：HandlePromotionFailure参数</code>的设置值是否允许担保失败（Handle Promotion Failure）如果允 许，那会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小，如果大 于，将尝试进行一次Minor GC，尽管这次Minor GC是有风险的；如果小于，或者-XX： HandlePromotionFailure设置不允许冒险，那这时就要改为进行一次Full GC</p>
<h1 id="第四章虚拟机性能监控、故障处理工具"><a href="#第四章虚拟机性能监控、故障处理工具" class="headerlink" title="第四章虚拟机性能监控、故障处理工具"></a>第四章虚拟机性能监控、故障处理工具</h1><h2 id="基础故障处理工具"><a href="#基础故障处理工具" class="headerlink" title="基础故障处理工具"></a>基础故障处理工具</h2><h3 id="jps：虚拟机进程状况工具"><a href="#jps：虚拟机进程状况工具" class="headerlink" title="jps：虚拟机进程状况工具"></a>jps：虚拟机进程状况工具</h3><blockquote>
<p>jps可以列出正在运行的虚拟机进 程，并显示虚拟机执行主类（Main Class，main()函数所在的类）名称以及这些进程的本地虚拟机唯一 ID（LVMID，Local Virtual Machine Identifier）</p>
</blockquote>
<p>命令格式 </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jps [options] [hostid]</span><br></pre></td></tr></table></figure>

<p>执行案例</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210205146436.png" alt="image-20220210205146436"></p>
<p>主要选项</p>
<table>
<thead>
<tr>
<th>选项</th>
<th>作用</th>
</tr>
</thead>
<tbody><tr>
<td>-q</td>
<td>只输出LVMID,省略主类的名称</td>
</tr>
<tr>
<td>-m</td>
<td>输出虚拟机进程启东市传递给主类main()函数的参数</td>
</tr>
<tr>
<td>-l</td>
<td>输出主类的全名，如果进程执行的是JAR包，则输出JAR的路径</td>
</tr>
<tr>
<td>-v</td>
<td>输出虚拟机进程启动时的JVM参数</td>
</tr>
</tbody></table>
<h3 id="jstat：虚拟机统计信息监视工具"><a href="#jstat：虚拟机统计信息监视工具" class="headerlink" title="jstat：虚拟机统计信息监视工具"></a>jstat：虚拟机统计信息监视工具</h3><blockquote>
<p>jstat（JVM Statistics Monitoring Tool）是用于监视虚拟机各种运行状态信息的命令行工具。它可 以显示本地或者远程[1]虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据，在没有 GUI图形界面、只提供了纯文本控制台环境的服务器上，它将是运行期定位虚拟机性能问题的常用工 具</p>
</blockquote>
<p>命令格式</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jstat [option vmid [interval[s|ms] [count ]] ]</span><br></pre></td></tr></table></figure>

<p>例子</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jstat -gc 9924 250 25</span><br></pre></td></tr></table></figure>

<p>每250 毫秒查询一次进程9924垃圾收集状况，一共查询20次</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210205846919.png" alt="image-20220210205846919"></p>
<p>jstat的主要选项</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210104893.png" alt="image-20220210210104893"></p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210218821.png" alt="image-20220210210218821"></p>
<h3 id="jinfo-Java配置信息工具"><a href="#jinfo-Java配置信息工具" class="headerlink" title="jinfo: Java配置信息工具"></a>jinfo: Java配置信息工具</h3><blockquote>
<p>jinfo（Configuration Info for Java）的作用是实时查看和调整虚拟机各项参数。</p>
</blockquote>
<p>命令格式</p>
<p>jinfo [option] pid</p>
<p>例子查询CMSInitiatingOccupancyFraction参数值 </p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210455440.png" alt="image-20220210210455440"></p>
<h3 id="jmap-Java内存映像工具"><a href="#jmap-Java内存映像工具" class="headerlink" title="jmap:Java内存映像工具"></a>jmap:Java内存映像工具</h3><blockquote>
<p>jmap（Memory Map for Java）命令用于生成堆转储快照（一般称为heapdump或dump文件）。</p>
<p>map的作用并不仅仅是为了获取堆转储快照，它还可以查询finalize执行队列、Java堆和方法区的 </p>
<p>详细信息，如空间使用率、当前用的是哪种收集器等。 </p>
</blockquote>
<p>命令格式</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jmap [ option ] vmid</span><br></pre></td></tr></table></figure>

<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210210825073.png" alt="image-20220210210825073"></p>
<h3 id="jhat-虚拟机堆转储快照分析工具"><a href="#jhat-虚拟机堆转储快照分析工具" class="headerlink" title="jhat:虚拟机堆转储快照分析工具"></a>jhat:虚拟机堆转储快照分析工具</h3><blockquote>
<p>JDK提供jhat（JVM Heap Analysis Tool）命令与jmap搭配使用，来分析jmap生成的堆转储快照。</p>
</blockquote>
<h3 id="jstack：Java堆栈跟踪工具"><a href="#jstack：Java堆栈跟踪工具" class="headerlink" title="jstack：Java堆栈跟踪工具"></a>jstack：Java堆栈跟踪工具</h3><blockquote>
<p>jstack（Stack Trace for Java）命令用于生成虚拟机当前时刻的线程快照（一般称为threaddump或者 javacore文件）。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合，生成线程快照的 目的通常是定位线程出现长时间停顿的原因，如线程间死锁、死循环、请求外部资源导致的长时间挂 起等，都是导致线程长时间停顿的常见原因。线程出现停顿时通过jstack来查看各个线程的调用堆栈， 就可以获知没有响应的线程到底在后台做些什么事情，或者等待着什么资源。</p>
</blockquote>
<p>命令格式</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jstack [ option ] vmid</span><br></pre></td></tr></table></figure>

<p>主要选项</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220210211123180.png" alt="image-20220210211123180"></p>
<h1 id="第六章-类文件结构"><a href="#第六章-类文件结构" class="headerlink" title="第六章 类文件结构"></a>第六章 类文件结构</h1><h2 id="Class类文件的结构"><a href="#Class类文件的结构" class="headerlink" title="Class类文件的结构"></a>Class类文件的结构</h2><blockquote>
<p>Class文件是一组以8个字节为基础单位的二进制流，各个数据项目严格按照顺序紧凑地排列在文件之中，中间没有添加任何分隔符</p>
</blockquote>
<p>根据《Java虚拟机规范》的规定，Class文件格式采用一种类似于C语言结构体的伪结构来存储数 据，这种伪结构中只有两种数据类型：“无符号数”和“表”。</p>
<ul>
<li>无符号数 ：属于基本的数据类型，以u1, u2, u4, u8分别表示1个字节，2个字节，4个字节和8个字节的无符号数，无符号数可以用来描述数字，索引引用，数量值或者按照UTF-8编码构成字符串值</li>
<li>表 ： 由多个无符号数或者其他表作为数据项构成的符合数据类型，为了方便区分所有表的命名都习惯以”_info”结尾表用于描述有层次关系的复合结构的数据，整个Class文件本质上也可以视作是一张表</li>
</ul>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212201617415.png" alt="image-20220212201617415"></p>
<h4 id="魔数与Class文件的版本"><a href="#魔数与Class文件的版本" class="headerlink" title="魔数与Class文件的版本"></a>魔数与Class文件的版本</h4><blockquote>
<p>每个Class文件的前4个字节称为魔数,唯一的作用是确定该Class文件是否能被虚拟机所接受。</p>
</blockquote>
<p>紧挨着魔数的四个字节存储的是Class文件的版本号，第五和第六个字节是次版本号，第七和第八个字节是主版本号</p>
<h3 id="常量池"><a href="#常量池" class="headerlink" title="常量池"></a>常量池</h3><blockquote>
<p>紧接着主、次版本号之后的是常量池入口，常量池可以比喻为Class文件里的资源仓库，它是Class 文件结构中与其他项目关联最多的数据，通常也是占用Class文件空间最大的数据项目之一，另外，它 还是在Class文件中第一个出现的表类型数据项目。</p>
</blockquote>
<p>常量池中主要存放着两大类常量</p>
<ul>
<li>字面量 比较接近Java语言层面的常量概念</li>
<li>符号引用 主要包括<ul>
<li>·被模块导出或者开放的包（Package） </li>
<li>·类和接口的全限定名（Fully Qualified Name） </li>
<li>·字段的名称和描述符（Descriptor） </li>
<li>·方法的名称和描述符 </li>
<li>·方法句柄和方法类型（Method Handle、Method Type、Invoke Dynamic） </li>
<li>·动态调用点和动态常量（Dynamically-Computed Call Site、Dynamically-Computed Constant）</li>
</ul>
</li>
</ul>
<h3 id="访问标志"><a href="#访问标志" class="headerlink" title="访问标志"></a>访问标志</h3><blockquote>
<p>在常量池结束之后，紧接着的2个字节代表访问标志（access_flags），这个标志用于识别一些类或 者接口层次的访问信息，包括：这个Class是类还是接口；是否定义为public类型；是否定义为abstract 类型；如果是类的话，是否被声明为final</p>
</blockquote>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212202502170.png" alt="image-20220212202502170"></p>
<h1 id="第七章-虚拟机类加载机制"><a href="#第七章-虚拟机类加载机制" class="headerlink" title="第七章 虚拟机类加载机制"></a>第七章 虚拟机类加载机制</h1><h2 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h2><blockquote>
<p> 类加载机制：java虚拟机把描述类的数据从Class文件加载到内存，并对数据进行校验、转换解析和初始化，最终形成可以被虚拟机直接使用的Java类型的过程</p>
</blockquote>
<h2 id="类加载的时机"><a href="#类加载的时机" class="headerlink" title="类加载的时机"></a>类加载的时机</h2><p>一个类型从被加载到虚拟机内存中开始，到卸载内存为止会经历</p>
<p>加载 -&gt; 验证 -&gt; 准备 -&gt; 解析 -&gt; 初始化 -&gt; 使用 -&gt; 卸载七个阶段</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212204702191.png" alt="image-20220212204702191"></p>
<p><strong>上图中 加载 验证 准备 初始化 卸载五个阶段的顺序是可以确定的，类的加载过程必须按照这种顺序按部就班的开始，它在某些情况下可以在初始化阶段之后再开始， 这是为了支持Java语言的运行时绑定特性（也称为动态绑定或晚期绑定）。</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">对于初始化阶段，《Java虚拟机规范》 则是严格规定了有且只有六种情况必须立即对类进行“初始化”（而加载、验证、准备自然需要在此之前开始）： </span><br><span class="line"></span><br><span class="line">1）遇到new、getstatic、putstatic或invokestatic这四条字节码指令时，如果类型没有进行过初始 化，则需要先触发其初始化阶段。能够生成这四条指令的典型Java代码场景有： 使用new关键字实例化对象的时候。 读取或设置一个类型的静态字段（被final修饰、已在编译期把结果放入常量池的静态字段除外）的时候。调用一个类型的静态方法的时候。 </span><br><span class="line"></span><br><span class="line">2）使用java.lang.reflect包的方法对类型进行反射调用的时候，如果类型没有进行过初始化，则需要先触发其初始化。</span><br><span class="line"></span><br><span class="line">3）当初始化类的时候，如果发现其父类还没有进行过初始化，则需要先触发其父类的初始化。 </span><br><span class="line"></span><br><span class="line">4）当虚拟机启动时，用户需要指定一个要执行的主类（包含main()方法的那个类），虚拟机会先初始化这个主类。 </span><br><span class="line"></span><br><span class="line">5）当使用JDK 7新加入的动态语言支持时，如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄，并且这个方法句柄对应的类没有进行过初始化，则需要先触发其初始化。 </span><br><span class="line"></span><br><span class="line">6）当一个接口中定义了JDK 8新加入的默认方法（被default关键字修饰的接口方法）时，如果有这个接口的实现类发生了初始化，那该接口要在其之前被初始化。 </span><br></pre></td></tr></table></figure>

<h2 id="类加载的过程"><a href="#类加载的过程" class="headerlink" title="类加载的过程"></a>类加载的过程</h2><h3 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h3><p>java属于类加载过程中的一个阶段</p>
<p>加载阶段虚拟机会完成三件事</p>
<ol>
<li>通过一个类的全限定名来获取定义此类的二进制字节流</li>
<li>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构</li>
<li>在内存中生成一个代表这个类的java.lang.Class对象，作为方法区这个类的各种数据的访问入口</li>
</ol>
<h3 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h3><blockquote>
<p>目的： 确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求</p>
</blockquote>
<h4 id="验证阶段-—-gt-四个阶段的检验动作"><a href="#验证阶段-—-gt-四个阶段的检验动作" class="headerlink" title="验证阶段 —&gt; 四个阶段的检验动作"></a>验证阶段 —&gt; 四个阶段的检验动作</h4><ol>
<li><p>文件格式验证 ：目的 保证输入的字节流能正确地解析并存储于方法区之内</p>
</li>
<li><p>元数据验证 ： 对字节码描述的信息进行语义分析</p>
<p> 验证点 </p>
<ul>
<li>这个类是否有父类</li>
<li>这个类的父类是否继承了不允许被继承的类</li>
<li>如果这个不是抽象类，是否实现了其父类或接口之中要求实现的所有方法</li>
<li>类中的字段、方法是否与父类产生矛盾</li>
</ul>
</li>
</ol>
<p>​     主要目的： 对类的元数据信息进行语义校验</p>
<ol start="3">
<li><p>字节码验证 主要目的是通过数据流分析和控制流分析，确定 程序语义是合法的、符合逻辑的</p>
<ul>
<li><p>保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作，例如不会出现类似于“在操作 栈放置了一个int类型的数据，使用时却按long类型来加载入本地变量表中”这样的情况。 </p>
</li>
<li><p>保证任何跳转指令都不会跳转到方法体以外的字节码指令上。 </p>
</li>
<li><p>保证方法体中的类型转换总是有效的，例如可以把一个子类对象赋值给父类数据类型，这是安全 的，但是把父类对象赋值给子类数据类型，甚至把对象赋值给与它毫无继承关系、完全不相干的一个 数据类型，则是危险和不合法的。</p>
</li>
</ul>
</li>
<li><p>符号引用验证</p>
<p>  发生在虚拟机将符号引用转化为直接引用的时候，转发动作在解析阶段中发生。可以看作是对类自身以外（常量池中的各种符号引用）的各类信息进行匹配性校验 -&gt;该类是否缺少或者被禁止访问它依赖的某些外部 类、方法、字段等资源。</p>
<p> 主要校验内容</p>
<ul>
<li>符号引用中通过字符穿描述的全限定名是否都能找到对应的类</li>
<li>在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段</li>
<li>符号引用中的类 字段 方法 的可访问性</li>
</ul>
</li>
</ol>
<h3 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h3><blockquote>
<p>准备阶段是正式为类中定义的变量（即静态变量，被static修饰的变量）分配内存并设置类变量初 始值的阶段</p>
</blockquote>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220212213748066.png" alt="image-20220212213748066"></p>
<h3 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h3><blockquote>
<p>解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程</p>
</blockquote>
<p>符号引用和直接引用</p>
<p>·符号引用（Symbolic References）：符号引用以一组符号来描述所引用的目标，符号可以是任何形式的字面量，只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关，引用的目标并不一定是已经加载到虚拟机内存当中的内容。各种虚拟机实现的内存布局可以各不相同，但是它们能接受的符号引用必须都是一致的，因为符号引用的字面量形式明确定义在《Java虚拟机规范》的Class文件格式中。 </p>
<p>·直接引用（Direct References）：直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局直接相关的，同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用，那引用的目标必定已经在虚拟机的内存中存在</p>
<h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><blockquote>
<p>直到初始化阶段，Java虚拟机才真正开始执行类中编写的Java程序代码，将主导权移交给应用程序。</p>
</blockquote>
<h2 id="类加载器"><a href="#类加载器" class="headerlink" title="类加载器"></a>类加载器</h2><blockquote>
<p>Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节 流”这个动作放到Java虚拟机外部去实现，以便让应用程序自己决定如何去获取所需的类。实现这个动 作的代码被称为“类加载器”（Class Loader）。 </p>
<p>— 《深入理解Java虚拟机》</p>
</blockquote>
<h3 id="类与类加载器"><a href="#类与类加载器" class="headerlink" title="类与类加载器"></a>类与类加载器</h3><p>类加载器虽然只用于实现类的加载动作，但它在Java程序中起到的作用却远超类加载阶段</p>
<p>对于任意一个类，都必须由加载它的类加载器和这个类本身一起共同确立在其Java虚拟机中的唯一性，都拥有一个独立的类名称空间</p>
<p>比较两个类是否“相等”，只有在两个类是由同一个类加载器加载的前题下才有意义</p>
<h3 id="双亲委派机制"><a href="#双亲委派机制" class="headerlink" title="双亲委派机制"></a>双亲委派机制</h3><p>从java虚拟机的角度来看只存在两种不同的类加载器： </p>
<ol>
<li>启动类加载器(BootstrapClassLoader)  C++语言实现，是虚拟机自身的一部分</li>
<li>其他所有的类加载器  Java语言实现，独立存在于虚拟机外部，全部继承于java.lang.ClassLoader</li>
</ol>
<p>从Java开发人员的角度来看：</p>
<p>绝大多数Java程序都会使用到以下3个系统提供的类加载器来进行加载：</p>
<ol>
<li><p>启动类加载器(Bootstrap Class Loader) : 存放在<JAVA_HOME>\lib目录，或者被-Xbootclasspath参数所指定的路径中存放的，而且是Java虚拟机能够 识别的（按照文件名识别，如rt.jar、tools.jar，名字不符合的类库即使放在lib目录中也不会被加载）类库加载到虚拟机的内存中</p>
</li>
<li><p>扩展类加载器(Extension Class Loader) : 负责加载<JAVA_HOME>\lib\ext目录中，或者被java.ext.dirs系统变量所指定的路径中所有的类库,是在类sun.misc.Launcher$ExtClassLoader 中以Java代码的形式实现</p>
</li>
<li><p>应用程序类加载器(Application Class Loader) : 由sun.misc.Launcher$AppClassLoader来实现，负责加载用户类路径(ClassPath)上所有的类库<strong>如果应用程序中没有 自定义过自己的类加载器，一般情况下这个就是程序中默认的类加载器</strong></p>
</li>
</ol>
<p><strong>双亲委派机制</strong></p>
<blockquote>
<p>当一个类加载器收到类加载请求时，他首先不是自己尝试加载这个类，而是委派给他的父类加载器完成，每一层的类加载器都是这样，因此所有的类加载请求都应传送到最顶层的启动类夹在其中，只要当父加载器无法完成这个加载请求时，子加载器才会自己尝试去完成加载。</p>
</blockquote>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216200819649.png" alt="image-20220216200819649"></p>
<h1 id="第八章-虚拟机字节码执行引擎"><a href="#第八章-虚拟机字节码执行引擎" class="headerlink" title="第八章 虚拟机字节码执行引擎"></a>第八章 虚拟机字节码执行引擎</h1><h2 id="运行时栈帧结构"><a href="#运行时栈帧结构" class="headerlink" title="运行时栈帧结构"></a>运行时栈帧结构</h2><p>java虚拟机以方法为最基本的执行单元，“栈帧（Stack Frame）”则是用于支持虚拟机进行方法调用和方法执行背后的数据结构，也是虚拟机运行时数据区中虚拟机栈的栈元素。栈帧存储了方法的局部变量表，操作数栈，动态连接和方法返回地址等信息。</p>
<p>每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。 在编译Java程序源码的时候，栈帧中需要多大的局部变量表，需要多深的操作数栈就已经被分析计算出来，并且写入到方法表的Code属性之中。换言之，一个栈帧需要分配多少内存，并不会受到程序运行期变量数据的影响，而仅仅取决于程序源码和具体的虚拟机实现的栈内存布局形式。</p>
<p>在活动线程中，只有位于栈顶的栈帧才是生效的称为“当前栈帧”，与这个栈帧相关联的方法被称为“当前方法”，执行引擎运行的所有字节码指令都只针对当前栈帧进行操作</p>
<h3 id="局部变量表"><a href="#局部变量表" class="headerlink" title="局部变量表"></a>局部变量表</h3><p>局部变量表是一组变量值到的存储空间，用于存储方法参数和方法内部定义的局部变量。</p>
<p>java程序被编译为Class文件时，就在方法的Code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量</p>
<p>局部变量表的容量以变量槽为最小单位</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216210635519.png" alt="image-20220216210635519"></p>
<p>Java通过索引的方式使用局部变量表，访问32位数据类型的变量，索引N代表使用第N个变量槽，64位数据类型的变量，索引N代表使用第N和N+1两个变量槽</p>
<p>一个方法调用时，java虚拟机会使用局部变量表来完成参数值到参数变量的传递过程。如果执行实例方法，那么局部变量表中第0位索引变量槽默认存储用于传递方法所属对象的引用。其余参数按参数表顺序排列，占用从1开始的局部变量槽，参数表分配完毕后，再根据方法体内部定义的变量顺序和作用域分配其余的变量槽。 </p>
<p>为了尽可能节省栈帧耗用的内存空间，局部变量表中的变量槽是可以重用的</p>
<h3 id="操作数栈"><a href="#操作数栈" class="headerlink" title="操作数栈"></a>操作数栈</h3><p>操作数栈（Operand Stack）也常被称为操作栈，它是一个后入先出（Last In First Out，LIFO）栈</p>
<p>同局部变量表一样，操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中</p>
<p>大多虚拟机的实现里都会进行一些优化处理，令两个栈帧出现一部分重叠。让下面栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起，这样做不仅节约了一些空间，更重要的是在进行方法调用时就可以直接共用一部分数据，无须进行额外的参数复制传递了</p>
<p><img src="https://image-1305623006.cos.ap-nanjing.myqcloud.com/image-20220216212130215.png" alt="image-20220216212130215"></p>
<h3 id="动态连接"><a href="#动态连接" class="headerlink" title="动态连接"></a>动态连接</h3><p>每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用，持有这个引用是为了支持方法调用过程中的动态连接（Dynamic Linking）。</p>

    </div>

    
    
    

    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>本文作者： </strong>yorn
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="http://wang-chen1119.gitee.io/myblog/2022/10/28/Java%E8%99%9A%E6%8B%9F%E6%9C%BA/" title="深入理解JVM读书笔记">http://wang-chen1119.gitee.io/myblog/2022/10/28/Java虚拟机/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

          <div class="post-tags">
              <a href="/myblog/tags/JVM/" rel="tag"><i class="fa fa-tag"></i> JVM</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/myblog/2022/10/28/python%E5%AE%9E%E7%8E%B0%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%8C%E6%88%90%E5%9F%BA%E6%9C%AC%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/" rel="prev" title="python实现操作数据库完成基本的增删改查">
                  <i class="fa fa-chevron-left"></i> python实现操作数据库完成基本的增删改查
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/myblog/2022/10/28/%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/" rel="next" title="Java基础数据类型">
                  Java基础数据类型 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">yorn</span>
</div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/myblog/js/comments.js"></script><script src="/myblog/js/utils.js"></script><script src="/myblog/js/motion.js"></script><script src="/myblog/js/next-boot.js"></script><script src="/myblog/js/bookmark.js"></script>

  





  





<script src="/myblog/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/","tagMode":false});</script></body>
</html>
